////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2003-2006 Adobe Macromedia Software LLC and its licensors.
//  All Rights Reserved. The following is Source Code and is subject to all
//  restrictions on such code as contained in the End User License Agreement
//  accompanying this product.
//
////////////////////////////////////////////////////////////////////////////////

package mx.core
{

import flash.display.Sprite;
import flash.events.Event;
import flash.utils.Dictionary;

import mx.utils.NameUtil;

/**
 *  FlexSprite is a subclass of the Player's Sprite class
 *  and the superclass of UIComponent.
 *  It overrides the <code>toString()</code> method
 *  to return a string indicating the location of the object
 *  within the hierarchy of DisplayObjects in the application.
 */
public dynamic class FlexSprite extends Sprite
{
    //include "../core/Version.as";

	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------

    /**
     *  Constructor.
	 *
	 *  <p>Sets the <code>name</code> property to a string
	 *  returned by the <code>createUniqueName()</code>
	 *  method of the mx.utils.NameUtils class.</p>
	 *
	 *  <p>This string is the name of the object's class concatenated
	 *  with an integer that is unique within the application,
	 *  such as <code>"Button17"</code>.</p>
	 *
	 *  @see flash.display.DisplayObject#name
	 *  @see mx.utils.NameUtil#createUniqueName()
     */
    public function FlexSprite()
	{
		super();
		
		try
		{
			name = NameUtil.createUniqueName(this);
		}
		catch(e:Error)
		{
			// The name assignment above can cause the RTE
			//   Error #2078: The name property of a Timeline-placed
			//   object cannot be modified.
			// if this class has been associated with an asset
			// that was created in the Flash authoring tool.
			// The only known case where this is a problem is when
			// an asset has another asset PlaceObject'd onto it and
			// both are embedded separately into a Flex application.
			// In this case, we ignore the error and toString() will
			// use the name assigned in the Flash authoring tool.
		}
		
	}

	//--------------------------------------------------------------------------
	//
	//  Overridden methods
	//
	//--------------------------------------------------------------------------

    /**
	 *  Returns a string indicating the location of this object
	 *  within the hierarchy of DisplayObjects in the Application.
	 *  This string, such as <code>"MyApp0.HBox5.Button17"</code>,
	 *  is built by the <code>displayObjectToString()</code> method
	 *  of the mx.utils.NameUtils class from the <code>name</code>
	 *  property of the object and its ancestors.
	 *  
	 *  @return A String indicating the location of this object
	 *  within the DisplayObject hierarchy. 
	 *
	 *  @see flash.display.DisplayObject#name
	 *  @see mx.utils.NameUtil#displayObjectToString()
     */
    override public function toString():String
	{
		return NameUtil.displayObjectToString(this);
	}
	
	/**
	 * All the code above is from the normal FlexSprite class in the Flex framework.
	 * All the code below is new stuff to keep track of event listeners. Notice how tiny the original FlexSprite
	 * class is. That makes it awesome to monkey patch because you don't have to reproduce a ton of code in
	 * your monkey patched class. If you're trying to get core functionality into ever class in the
	 * Flex framework and you don't need stuff that's specific to UIComponent, then the easiest way is probably to
	 * monkey patch FlexSprite. Otherwise you can monkey patch UIComponent, but that's a bit more complex.
	 */
	 
	 /**
	 * The array of listeners that we keep track of interally.
	 */
	private var listeners:Array = [];
	
	[Bindable("eventListenersUpdated")]
	public function get eventListeners():Array {
		return listeners;
	}
	
	override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void {
		var listenerDictionary:Dictionary = new Dictionary();
		var tracker:ListenerTracker = new ListenerTracker(type, listener, useCapture, priority, useWeakReference);
		
		var stackTrace:String = new Error().getStackTrace();
	
		var secondLine:String = stackTrace.split("\n")[2];
		
		var lineNumber:String = secondLine.substring(secondLine.lastIndexOf(":") + 1, secondLine.length-1);
		var classAndMethod:String = secondLine.substring(3, secondLine.indexOf("["));
		
		var classMethodSplit:Array = classAndMethod.split("/");
		
		var className:String = classMethodSplit[0];
		className = className.substr(1);
		
		var methodName:String = classMethodSplit[1];
		
		if(methodName == null) {
			methodName = "constructor";
			className = className.substr(0, className.length - 2);
		}
		else {
			methodName = methodName.substr(0, methodName.length-2);
		}
		
		tracker.callingClass = className;
		tracker.callingLineNumber = Number(lineNumber);
		tracker.callingMethod = methodName;

		
		listeners.push(tracker);
		
		super.addEventListener(type, listener, useCapture, priority, useWeakReference);
		
		dispatchEvent(new Event("eventListenersUpdated"));
	}
	
	override public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void {
		
		for each(var tracker:ListenerTracker in listeners) {
			if(tracker.listener == listener && tracker.type == type && tracker.useCapture == useCapture) {
				listeners.splice(listeners.indexOf(tracker), 1);
			}
		}
		
		super.removeEventListener(type, listener, useCapture);
		
		dispatchEvent(new Event("eventListenersUpdated"));
	}
	
	public function getEventListeners(type:String):Array {
		var returnArray:Array = new Array();
		
		for each(var tracker:ListenerTracker in listeners) {
			if(tracker.type == type) {
				returnArray.push(tracker);
			}
		}
		
		return returnArray;
	}
	
	public function getAllEventListeners():Array {
		return listeners;
	}
	
	public function removeAllEventListeners():void {
		for(var i:int=listeners.length-1; i>=0; i--) {
			var tracker:ListenerTracker = listeners[i];	
			
			if(tracker)
				removeEventListener(tracker.type, tracker.listener, tracker.useCapture);
		}
	}
	
	public function removeEventListeners(type:String):void {
		for(var i:int=listeners.length-1; i>=0; i--) {
			var tracker:ListenerTracker = listeners[i];	
			if(tracker.type == type) {
				removeEventListener(tracker.type, tracker.listener, tracker.useCapture);
			}
		}
	}
}
}